/*
echo server with select.
**/

#include <define.h>
#include <error.h>
#include <wrapfunc.h>
#include <sockfunc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>


int
main() {
	struct sockaddr_in local_addr;
	memset(&local_addr, 0, sizeof(local_addr));
	local_addr.sin_family = AF_INET;
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	local_addr.sin_port = htons(SERV_PORT);

	int listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	Bind(listenfd, (struct sockaddr*)&local_addr, sizeof(local_addr));
	Listen(listenfd, LISTENQ);
	error_msg("wait for clients");

	int client_fds[FD_SETSIZE], clientindex, max_clientindex, maxfd, readynum;
	fd_set rset, allset;
	struct sockaddr_in connaddr;
	socklen_t len;
	int connfd;
	char buf[MAX_LINE];
	ssize_t n;

	max_clientindex = -1;
	memset(client_fds, -1, sizeof(client_fds));
	readynum = 0;
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);
	maxfd = listenfd;

	for (;;) {
		rset = allset;
		readynum = Select(maxfd+1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) {
			len = sizeof(connaddr);
			connfd = Accept(listenfd, (struct sockaddr*)&connaddr, &len);
			error_msg("new connection fd:%d %s", connfd, sock_ntop((struct sockaddr*)&connaddr, len));

			clientindex = -1;
			for (int i = 0; i < FD_SETSIZE; i++) {
				if (client_fds[i] < 0) {
					clientindex = i;
					break;
				} 
			}
			if (clientindex < 0) {
				Close(connfd);
				error_msg("too many fds so close fd:%d", connfd);
			} else {
				client_fds[clientindex] = connfd;
				if (clientindex > max_clientindex)
					max_clientindex = clientindex;
				if (connfd > maxfd)
					maxfd = connfd;
				FD_SET(connfd, &allset);
			}

			if (--readynum <= 0)
				continue;
		}

		for (int i = 0; i <= max_clientindex; i++) {
			connfd = client_fds[i];
			if (connfd < 0)
				continue;
			if (FD_ISSET(connfd, &rset)) {
				if ((n=Read(connfd, buf, sizeof(buf))) == 0) {
					error_msg("connection fd:%d is closed", connfd);
					FD_CLR(connfd, &allset);
					Close(connfd);
					client_fds[i] = -1;
				} else {
					Writen(connfd, buf, n);
				}

				if (--readynum <= 0)
					break;
			}
		}
	}

	Close(listenfd);
	return 0;
}
